import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Observable;
import java.util.Observer;

import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;

public class DictionaryClient implements Observer {
	private Socket socket;
	private DictionaryClientResponseReceiver receiver;
	private DictionaryClientFrame frame;
	private static final String LOCAL_HOST = "127.0.0.1";
	private static final int SERVERPORT = 44444;

	private ObjectOutputStream outStream;
	private ObjectInputStream inStream;

	public static void main(String args[]) {
		new DictionaryClient();
	}

	public DictionaryClient() {
		System.setProperty("javax.net.ssl.keyStore", "clientkeystore.jks");
		System.setProperty("javax.net.ssl.keyStorePassword", "frafele");
		System.setProperty("javax.net.ssl.trustStore", "clienttruststore.jks");
		System.setProperty("javax.net.ssl.trustStorePassword", "frafele");

		try {
			initConnection();

			// AVVIO INTERFACCIA GRAFICA
			EventQueue.invokeLater(new Runnable() {
				public void run() {
					try {
						frame = new DictionaryClientFrame();
						frame.setVisible(true);
						frame.setSendListener(new SendMessageListener());
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			});

			// GESTORE DELLA CHIUSURA DELL'APPLICAZIONE
			Runtime.getRuntime().addShutdownHook(new Thread() {
				@Override
				public void run() {

					try {
						outStream.writeObject(MessageFactory
								.GenerateCloseMessage());
						outStream.flush();

						destroyConnection();
					} catch (IOException e1) {
					}
					System.out.println("Chiusura connessione");
				}
			});
		} catch (IOException e) {
			System.out.println("Connessione fallita");
			destroyConnection();
		}

	}

	private void initConnection() throws IOException {
		setSocket(createSocket(LOCAL_HOST, SERVERPORT));
		outStream = new ObjectOutputStream(new BufferedOutputStream(getSocket()
				.getOutputStream()));
		outStream.flush();
		inStream = new ObjectInputStream(new BufferedInputStream(getSocket()
				.getInputStream()));
		// Avvio thread che si occupa di gestire le risposte e mi registro
		// in ascolto
		receiver = new DictionaryClientResponseReceiver(inStream);
		receiver.addObserver(this);
		new Thread(receiver).start();
	}

	private void destroyConnection() {

		try {
			if (outStream != null)
				outStream.close();
			if (inStream != null)
				inStream.close();
			if (receiver != null)
				receiver.setStop();
			if (socket != null)
				socket.close();
		} catch (IOException e) {
		}

	}

	private Socket createSocket(String host, int port) throws IOException {
		SocketFactory factory = SSLSocketFactory.getDefault();
		Socket sock = factory.createSocket(host, port);
		return sock;
	}

	public Socket getSocket() {
		return socket;
	}

	public void setSocket(Socket socket) {
		this.socket = socket;
	}

	/**
	 * Listener che gestisce l'azione di invio di un messaggio. E' associato al
	 * bottone send dell'interfaccia.
	 * 
	 */
	private class SendMessageListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			new Thread(new Runnable() {

				@Override
				public void run() {
					OperationType operation = frame.getMessageType();
					Message requestMessage = null;
					// Creo la richiesta a seconda dell'operazione selezionata
					// sull'interfaccia
					switch (operation) {
					case CLOSESOCKET:
						requestMessage = MessageFactory.GenerateCloseMessage();
						break;
					case INSERT:
						String itaWord = frame.getItalianWord();
						String engWord = frame.getEnglishWord();
						if (itaWord.equals("") || engWord.equals(""))
							frame.setErrorOnParameters();
						else {
							requestMessage = MessageFactory
									.GenerateInsertRequest(itaWord, engWord);
						}
						break;
					case PING:
						requestMessage = MessageFactory.GeneratePingRequest();
						break;
					case TRANSLATE:
						String itaW = frame.getItalianWord();
						if (itaW.equals(""))
							frame.setErrorOnParameters();
						else {
							requestMessage = MessageFactory
									.GenerateTranslateRequest(itaW);
						}
						break;

					}
					// Invio il messaggio
					if (requestMessage != null) {
						try {
							outStream.writeObject(requestMessage);
							outStream.flush();
						} catch (IOException e1) {
							// Provo a reinizializzare la connessione
							destroyConnection();
							try {
								initConnection();
								// Provo a inviare di nuovo il messaggio
								outStream.writeObject(requestMessage);
								outStream.flush();
							} catch (IOException e2) {
								frame.setServerNotAvailable();
							}
						}
					}
				}

			}).start();
		}
	}

	@Override
	public void update(Observable o, Object arg) {
		Message m = (Message) arg;
		if (m != null)
			frame.showMessage(m);
	}
}
